package com.iplant.base.filter;

import com.iplant.base.controller.BaseController;
import com.iplant.base.po.BaseUser;
import com.iplant.base.utils.SessionConstants;
import com.iplant.base.utils.log.TraceLoggerTool;
import com.iplant.base.utils.general.StringUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Calendar;
import java.util.Objects;

@Component
@ServletComponentScan
@WebFilter(urlPatterns = {"/api/*"}, filterName = "TraceLogFilter")
public class TraceLogFilter extends OncePerRequestFilter {
    private static final Logger logger = LoggerFactory.getLogger(OncePerRequestFilter.class);
    private static final String NEED_TRACE_PATH_PREFIX = "/api/";
    private static final String IGNORE_CONTENT_TYPE = "multipart/form-data";

    private static final String FILTER_APPLIED = "__spring_security_demo_trace_Filter_filterApplied";


    @SuppressWarnings({"RedundantThrows", "ConstantValue", "NullableProblems"})
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String wURI = "";
        try {

            if (request.getAttribute(FILTER_APPLIED) != null) {
                if (filterChain != null)
                    filterChain.doFilter(request, response);
                return;
            }
            request.setAttribute(FILTER_APPLIED, true);

            if (IGNORE_CONTENT_TYPE.equalsIgnoreCase(request.getContentType())) {
                if (filterChain != null)
                    filterChain.doFilter(request, response);
                return;
            }
            wURI = request.getRequestURI();

            String wProjectName = BaseController.GetProjectName(request);
            if (!wURI.startsWith(wProjectName + NEED_TRACE_PATH_PREFIX)) {
                if (filterChain != null) {
                    filterChain.doFilter(request, response);
                }
                return;
            }





            if (!isRequestValid(request)) {
                if (filterChain != null)
                    filterChain.doFilter(request, response);
                return;
            }
            if (!(request instanceof ContentCachingRequestWrapper)) {
                request = new ContentCachingRequestWrapper(request);
            }
            if (!(response instanceof ContentCachingResponseWrapper)) {
                response = new ContentCachingResponseWrapper(response);
            }
            long startTime = System.currentTimeMillis();
            Calendar wStartTime = Calendar.getInstance();
            try {
                filterChain.doFilter(request, response);
            } finally {

                BaseUser wBMSEmployee = null;
                HttpSession session = request.getSession();
                if (session.getAttribute(SessionConstants.SessionUser) != null) {
                    wBMSEmployee = (BaseUser) session.getAttribute(SessionConstants.SessionUser);
                }
                if (wBMSEmployee == null)
                    wBMSEmployee = new BaseUser();

                if (wBMSEmployee.ID > 0 || wBMSEmployee.ID == BaseUser.SysAdmin.ID) {

                    try {
                        if (wURI.startsWith(wProjectName)) {
                            wURI = wURI.substring(wProjectName.length());
                        }

                        int wInterval=(int)(System.currentTimeMillis() - startTime);

                        String wResponseBody = getResponseBody(response);

                        TraceLoggerTool.SaveApiLogNew(wBMSEmployee,  BaseController.GetRealProjectName(request), wURI,
                                request.getMethod(),  StringUtils.toJSONString(request.getParameterMap()),
                                getRequestBody(request), wResponseBody, wStartTime, Calendar.getInstance(),
                                wInterval);

                    } catch (Exception e) {
                        logger.error("Save API Log  url:" + wURI + " Error:" + e);
                    }
                }
                updateResponse(response);
            }
        } catch (Exception e) {
            logger.error("TraceLog url:" + wURI + " Error:", e);
        }

    }

    private boolean isRequestValid(HttpServletRequest request) {
        try {
            new URI(request.getRequestURL().toString());
            return true;
        } catch (URISyntaxException ex) {
            return false;
        }
    }

    private String getRequestBody(HttpServletRequest request) {
        String requestBody = "";
        ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper != null) {
            try {
                requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
            } catch (IOException e) {
                // NOOP
            }
        }
        return requestBody;
    }

    private String getResponseBody(HttpServletResponse response) {
        String responseBody = "";
        ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response,
                ContentCachingResponseWrapper.class);
        if (wrapper != null) {
            try {
                responseBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
            } catch (IOException e) {
                // NOOP
            }
        }
        return responseBody;
    }

    private void updateResponse(HttpServletResponse response) throws IOException {
        ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response,
                ContentCachingResponseWrapper.class);
        Objects.requireNonNull(responseWrapper).copyBodyToResponse();
    }

}
